import time
from flask import Flask, render_template, request, redirect, url_for, flash, jsonify, send_file
import os
import threading
from pathlib import Path
import subprocess
import sys
import json
import tempfile
import uuid
from dotenv import load_dotenv

# 添加src目录到Python路径
sys.path.append(os.path.join(os.path.dirname(__file__), 'src'))

# 设置标准输出和标准错误流的编码
sys.stdout.reconfigure(encoding='utf-8')
sys.stderr.reconfigure(encoding='utf-8')

# 加载.env文件中的环境变量
load_dotenv()

# 导入数据库模块
from src.processors.sora2_video_handler import Sora2VideoHandler
from src.utils.video_database import VideoDatabase, KeywordConfig

app = Flask(__name__)
app.secret_key = 'your-secret-key-here'

# 全局变量存储任务状态
tasks = {}
task_counter = 0

# 文本到视频任务状态
t2v_tasks = {}
t2v_task_counter = 0

# 女性图片生成视频任务状态
female_video_tasks = {}
female_video_task_counter = 0

# 本地文件任务状态
local_tasks = {}
local_task_counter = 0

# 添加图片编辑任务状态
image_edit_tasks = {}
image_edit_task_counter = 0

# 添加Excel处理任务状态
excel_tasks = {}
excel_task_counter = 0

# Sora2视频生成任务状态
sora2_tasks = {}

# 添加批量对口型视频处理任务状态
batch_lipsync_tasks = {}
batch_lipsync_task_counter = 0

# 缓存文件路径
CACHE_FILE = 'input_cache.json'


def load_cached_inputs():
    """加载缓存的输入路径"""
    try:
        if os.path.exists(CACHE_FILE):
            with open(CACHE_FILE, 'r', encoding='utf-8') as f:
                return json.load(f)
    except Exception as e:
        print(f"加载缓存失败: {e}")
    return {}


def save_cached_inputs(**kwargs):
    """保存输入路径到缓存"""
    try:
        # 先加载现有缓存
        cache_data = load_cached_inputs()
        
        # 更新缓存数据
        cache_data.update(kwargs)
        
        # 保存到文件
        with open(CACHE_FILE, 'w', encoding='utf-8') as f:
            json.dump(cache_data, f, ensure_ascii=False, indent=2)
    except Exception as e:
        print(f"保存缓存失败: {e}")


def run_video_generation(images_dir, audios_dir, output_dir, task_id, generation_type='ai'):
    """
    在后台线程中运行视频生成任务
    """
    try:
        tasks[task_id]['status'] = 'running'
        tasks[task_id]['message'] = '正在执行视频生成...'
        
        if generation_type == 'ai':
            # 使用AI方式生成视频（调用i2v_processor）
            from src.processors.i2v_processor import process_images_with_audio
            result, logs = process_images_with_audio(images_dir, audios_dir, output_dir, tasks[task_id])
        else:
            # 使用传统方式生成视频（调用video_generator）
            import subprocess
            import sys
            
            # 调用video_generator.py脚本
            cmd = [sys.executable, 'src/processors/video_generator.py', images_dir, audios_dir, output_dir]
            
            process = subprocess.Popen(
                cmd,
                stdout=subprocess.PIPE,
                stderr=subprocess.STDOUT,
                universal_newlines=True,
                encoding='utf-8',
                errors='replace',
                env={**os.environ}
            )
            
            # 实时捕获输出
            output_lines = []
            for line in process.stdout:
                clean_line = line.rstrip('\n\r')
                output_lines.append(clean_line)
                tasks[task_id]['log'] = '\n'.join(output_lines)
            
            process.wait()
            result = process.returncode == 0
            # 最终日志已在循环中更新
        
        if result:
            tasks[task_id]['status'] = 'completed'
            tasks[task_id]['message'] = '视频生成完成!'
        else:
            tasks[task_id]['status'] = 'failed'
            tasks[task_id]['message'] = '视频生成失败'
            
    except Exception as e:
        tasks[task_id]['status'] = 'failed'
        tasks[task_id]['message'] = f'执行出错: {str(e)}'
        # 更新日志
        if 'log' in tasks[task_id]:
            log_lines = tasks[task_id]['log'].split('\n') if tasks[task_id]['log'] else []
            log_lines.append(f'执行出错: {str(e)}')
            tasks[task_id]['log'] = "\n".join(log_lines)
        else:
            tasks[task_id]['log'] = f'执行出错: {str(e)}'


def run_local_file_generation(image_path, prompt, resolution, task_id, negative_prompt=""):
    """
    在后台线程中运行本地文件方式的视频生成任务
    """
    try:
        local_tasks[task_id]['status'] = 'running'
        local_tasks[task_id]['message'] = '正在执行视频生成...'
        
        # 导入新的处理模块
        from src.processors.i2v_processor import process_single_image_with_audio
        import os
        
        # 处理单个图片
        # 获取音频目录（这里假设一个默认的音频目录，可以根据需要修改）
        audios_dir = "audios"  # 默认音频目录
        output_dir = "output"  # 默认输出目录
        
        # 确保输出目录存在
        os.makedirs(output_dir, exist_ok=True)
        
        # 生成输出文件路径
        import pathlib
        image_name = pathlib.Path(image_path).stem
        output_path = os.path.join(output_dir, f"{image_name}.mp4")
        
        # 获取音频文件列表
        audio_files = []
        if os.path.exists(audios_dir):
            for ext in ('*.mp3', '*.wav', '*.aac', '*.flac', '*.m4a'):
                import glob
                audio_files.extend(glob.glob(os.path.join(audios_dir, ext)))
        
        # 处理图片
        result, logs = process_single_image_with_audio(
            image_path, prompt, resolution, audio_files, output_path, negative_prompt, duration=5.0
        )
        
        local_tasks[task_id]['log'] = logs
        
        if result:
            local_tasks[task_id]['status'] = 'completed'
            local_tasks[task_id]['message'] = '视频生成完成!'
            local_tasks[task_id]['video_url'] = output_path
        else:
            local_tasks[task_id]['status'] = 'failed'
            local_tasks[task_id]['message'] = '视频生成失败'
            
    except Exception as e:
        local_tasks[task_id]['status'] = 'failed'
        local_tasks[task_id]['message'] = f'执行出错: {str(e)}'
        # 更新日志
        if 'log' in local_tasks[task_id]:
            log_lines = local_tasks[task_id]['log'].split('\n') if local_tasks[task_id]['log'] else []
            log_lines.append(f'执行出错: {str(e)}')
            local_tasks[task_id]['log'] = "\n".join(log_lines)
        else:
            local_tasks[task_id]['log'] = f'执行出错: {str(e)}'


def run_image_edit_task(image_path, prompt, negative_prompt, task_id):
    """
    在后台线程中运行图片编辑任务
    """
    try:
        image_edit_tasks[task_id]['status'] = 'running'
        image_edit_tasks[task_id]['message'] = '正在编辑图片...'
        
        # 导入图片编辑模块
        from src.processors.image_editor import process_image_edit
        import os
        
        # 确保输出目录存在
        output_dir = "output"
        os.makedirs(output_dir, exist_ok=True)
        
        # 生成输出文件路径
        import pathlib
        image_name = pathlib.Path(image_path).stem
        output_path = os.path.join(output_dir, f"{image_name}_edited_{uuid.uuid4().hex[:8]}.png")
        
        # 处理图片编辑
        result, message = process_image_edit(
            image_path, prompt, negative_prompt, output_path
        )
        
        if result:
            image_edit_tasks[task_id]['status'] = 'completed'
            image_edit_tasks[task_id]['message'] = '图片编辑完成!'
            image_edit_tasks[task_id]['output_path'] = output_path
        else:
            image_edit_tasks[task_id]['status'] = 'failed'
            image_edit_tasks[task_id]['message'] = f'图片编辑失败: {message}'
            
    except Exception as e:
        image_edit_tasks[task_id]['status'] = 'failed'
        image_edit_tasks[task_id]['message'] = f'执行出错: {str(e)}'


def update_excel_task_log(task_id, log_message):
    """
    更新Excel处理任务的日志
    
    Args:
        task_id (int): 任务ID
        log_message (str): 日志消息
    """
    if task_id in excel_tasks and 'log' in excel_tasks[task_id]:
        # 添加新日志行
        if excel_tasks[task_id]['log']:
            excel_tasks[task_id]['log'] += '\n' + log_message
        else:
            excel_tasks[task_id]['log'] = log_message
    elif task_id in excel_tasks:
        # 初始化日志
        excel_tasks[task_id]['log'] = log_message


def run_excel_processor_task(excel_path, audios_dir, output_dir, task_id):
    """
    在后台线程中运行Excel处理任务
    """
    try:
        task = excel_tasks[task_id]
        task['status'] = 'running'
        task['message'] = '正在处理Excel文件...'
        
        # 导入Excel处理模块
        from src.processors.excel_processor import process_match_videos, process_match_videos_new_method
        import os
        from dotenv import load_dotenv
        
        # 加载环境变量
        load_dotenv()
        
        # 获取API密钥
        api_key = os.getenv('DASHSCOPE_API_KEY')
        if not api_key:
            raise Exception("未找到DASHSCOPE_API_KEY环境变量")
        
        # 检查任务是否指定了使用新方法
        use_new_method = task.get('use_new_method', False)
        
        # 处理Excel中的比赛信息并生成视频
        if use_new_method:
            # 使用新方法（避免文本错误）
            results = process_match_videos_new_method(excel_path, audios_dir, output_dir, api_key, language="vi")
        else:
            # 使用原有方法
            results = process_match_videos(excel_path, audios_dir, output_dir, api_key, language="vi")
        
        # 更新任务状态
        task['status'] = 'completed' if results else 'failed'
        task['message'] = f'处理完成，共生成 {len([r for r in results if r["status"] == "success"])} 个视频'
        task['results'] = results
        
        # 检查是否有欠费错误
        has_arrearage = any('账户欠费' in str(r.get('message', '')) for r in results)
        task['results'] = {
            'data': results,
            'has_arrearage': has_arrearage
        }
        
    except Exception as e:
        excel_tasks[task_id]['status'] = 'failed'
        excel_tasks[task_id]['message'] = f'执行出错: {str(e)}'
        # 更新日志
        if 'log' in excel_tasks[task_id]:
            log_lines = excel_tasks[task_id]['log'].split('\n') if excel_tasks[task_id]['log'] else []
            log_lines.append(f'执行出错: {str(e)}')
            excel_tasks[task_id]['log'] = "\n".join(log_lines)
        else:
            excel_tasks[task_id]['log'] = f'执行出错: {str(e)}'


def run_excel_task_new_method(excel_path, audios_dir, output_dir, task_id):
    """
    在后台线程中运行Excel处理任务（新方法）
    """
    try:
        excel_tasks[task_id]['status'] = 'running'
        excel_tasks[task_id]['message'] = '正在处理Excel文件...'
        
        # 导入Excel处理模块
        from src.processors.excel_processor import process_match_videos_new_method
        import os
        from dotenv import load_dotenv
        load_dotenv()
        
        # 获取API密钥
        api_key = os.getenv('DASHSCOPE_API_KEY')
        if not api_key:
            raise Exception('未配置DASHSCOPE_API_KEY环境变量')
        
        # 处理Excel文件并生成视频（使用新方法）
        results = process_match_videos_new_method(
            excel_path=excel_path,
            audios_dir=audios_dir,
            output_dir=output_dir,
            api_key=api_key,
            language="zh"  # 默认使用中文
        )
        
        # 检查是否有欠费错误
        has_arrearage = False
        if isinstance(results, dict) and 'has_arrearage' in results:
            has_arrearage = results['has_arrearage']
        
        excel_tasks[task_id]['results'] = results
        excel_tasks[task_id]['has_arrearage'] = has_arrearage
        
        if has_arrearage:
            excel_tasks[task_id]['status'] = 'failed'
            excel_tasks[task_id]['message'] = '处理失败：检测到账户欠费'
        elif results:
            excel_tasks[task_id]['status'] = 'completed'
            excel_tasks[task_id]['message'] = 'Excel处理完成!'
        else:
            excel_tasks[task_id]['status'] = 'failed'
            excel_tasks[task_id]['message'] = 'Excel处理失败'
            
    except Exception as e:
        excel_tasks[task_id]['status'] = 'failed'
        excel_tasks[task_id]['message'] = f'执行出错: {str(e)}'
        # 更新日志
        if 'log' in excel_tasks[task_id]:
            log_lines = excel_tasks[task_id]['log'].split('\n') if excel_tasks[task_id]['log'] else []
            log_lines.append(f'执行出错: {str(e)}')
            excel_tasks[task_id]['log'] = "\n".join(log_lines)
        else:
            excel_tasks[task_id]['log'] = f'执行出错: {str(e)}'


def run_t2v_task(team_a, team_b, match_time, match_name, output_dir, task_id):
    """
    在后台线程中运行文本到视频任务
    """
    try:
        t2v_tasks[task_id]['status'] = 'running'
        t2v_tasks[task_id]['message'] = 'Đang xử lý tác vụ văn bản thành video...'
        
        # 导入 文本 đến video 处 lý模块
        from src.processors.t2v_processor import process_text_to_video
        
        # 处 lý văn bản đến video
        result, output_path = process_text_to_video(
            team_a, team_b, match_time, match_name, output_dir, t2v_tasks[task_id]
        )
        
        if result:
            t2v_tasks[task_id]['status'] = 'completed'
            t2v_tasks[task_id]['message'] = 'Tạo video hoàn tất!'
            t2v_tasks[task_id]['video_path'] = output_path
        else:
            t2v_tasks[task_id]['status'] = 'failed'
            t2v_tasks[task_id]['message'] = 'Tạo video thất bại'
            
    except Exception as e:
        t2v_tasks[task_id]['status'] = 'failed'
        t2v_tasks[task_id]['message'] = f'Lỗi khi thực thi: {str(e)}'
        # 更新日志
        if 'log' in t2v_tasks[task_id]:
            log_lines = t2v_tasks[task_id]['log'].split('\n') if t2v_tasks[task_id]['log'] else []
            log_lines.append(f'Lỗi khi thực thi: {str(e)}')
            t2v_tasks[task_id]['log'] = "\n".join(log_lines)
        else:
            t2v_tasks[task_id]['log'] = f'Lỗi khi thực thi: {str(e)}'


def run_t2v_excel_task(excel_path, output_dir, task_id):
    """
    在后台线程中运行Excel到视频任务
    """
    try:
        t2v_tasks[task_id]['status'] = 'running'
        t2v_tasks[task_id]['message'] = 'Đang xử lý nhiệm vụ Excel đến video...'
        
        # 导入 文本 đến video 处 lý模块
        from src.processors.t2v_processor import process_excel_to_videos
        
        # 处 lý Excel đến video
        result, output_data = process_excel_to_videos(
            excel_path, output_dir, t2v_tasks[task_id]
        )
        
        if result:
            t2v_tasks[task_id]['status'] = 'completed'
            t2v_tasks[task_id]['message'] = 'Video hàng loạt tạo thành công!'
            t2v_tasks[task_id]['output_data'] = output_data
        else:
            t2v_tasks[task_id]['status'] = 'failed'
            t2v_tasks[task_id]['message'] = 'Video hàng loạt tạo thất bại'
            
    except Exception as e:
        t2v_tasks[task_id]['status'] = 'failed'
        t2v_tasks[task_id]['message'] = f'Điều hành lỗi: {str(e)}'
        # 更新日志
        if 'log' in t2v_tasks[task_id]:
            log_lines = t2v_tasks[task_id]['log'].split('\n') if t2v_tasks[task_id]['log'] else []
            log_lines.append(f'Điều hành lỗi: {str(e)}')
            t2v_tasks[task_id]['log'] = "\n".join(log_lines)
        else:
            t2v_tasks[task_id]['log'] = f'Điều hành lỗi: {str(e)}'


def run_female_video_task(image_path, count, output_dir, task_id):
    """
    在后台线程中运行女性肖像视频生成任务
    """
    try:
        female_video_tasks[task_id]['status'] = 'running'
        female_video_tasks[task_id]['message'] = 'Đang xử lý nhiệm vụ tạo video từ hình ảnh nữ...'
        
        # 导入女性肖像视频处理模块
        from src.processors.female_portrait_video import process_female_portrait_video
        
        # 处 lý女性肖像 video
        result, output_paths = process_female_portrait_video(
            image_path, count, output_dir
        )
        
        if result:
            female_video_tasks[task_id]['status'] = 'completed'
            female_video_tasks[task_id]['message'] = f'Tạo video hoàn tất! Tổng cộng tạo {len(output_paths)} video'
            female_video_tasks[task_id]['video_paths'] = output_paths
        else:
            female_video_tasks[task_id]['status'] = 'failed'
            female_video_tasks[task_id]['message'] = 'Tạo video thất bại'
            
    except Exception as e:
        female_video_tasks[task_id]['status'] = 'failed'
        female_video_tasks[task_id]['message'] = f'Điều hành lỗi: {str(e)}'
        # 更新日志
        if 'log' in female_video_tasks[task_id]:
            log_lines = female_video_tasks[task_id]['log'].split('\n') if female_video_tasks[task_id]['log'] else []
            log_lines.append(f'Điều hành lỗi: {str(e)}')
            female_video_tasks[task_id]['log'] = "\n".join(log_lines)
        else:
            female_video_tasks[task_id]['log'] = f'Điều hành lỗi: {str(e)}'


@app.route('/')
def index():
    # 加载缓存的输入
    cached_inputs = load_cached_inputs()
    return render_template('index.html', cached_inputs=cached_inputs)


@app.route('/local-file')
def local_file():
    cached_inputs = load_cached_inputs()
    return render_template('local_file.html', cached_inputs=cached_inputs)


@app.route('/image-edit')
def image_edit():
    cached_inputs = load_cached_inputs()
    return render_template('image_edit.html', cached_inputs=cached_inputs)


@app.route('/excel-processor')
def excel_processor():
    cached_inputs = load_cached_inputs()
    return render_template('excel_processor.html', cached_inputs=cached_inputs)


@app.route('/generate', methods=['POST'])
def generate():
    global task_counter
    
    # 获取表单数据
    images_dir = request.form.get('images_dir')
    audios_dir = request.form.get('audios_dir')
    output_dir = request.form.get('output_dir')
    generation_type = request.form.get('generation_type', 'ai')  # 添加generation_type参数，默认为'ai'
    
    # 验证输入
    if not all([images_dir, audios_dir, output_dir]):
        flash('请填写所有路径字段', 'error')
        return redirect(url_for('index'))
    
    if not os.path.exists(images_dir):
        flash(f'图片目录不存在: {images_dir}', 'error')
        return redirect(url_for('index'))
        
    if not os.path.exists(audios_dir):
        flash(f'音频目录不存在: {audios_dir}', 'error')
        return redirect(url_for('index'))
    
    # 保存输入到缓存
    save_cached_inputs(
        images_dir=images_dir,
        audios_dir=audios_dir,
        output_dir=output_dir,
        generation_type=generation_type
    )
    
    # 创建新任务
    task_counter += 1
    task_id = task_counter
    
    tasks[task_id] = {
        'id': task_id,
        'status': 'pending',
        'message': '任务已提交，等待执行...',
        'images_dir': images_dir,
        'audios_dir': audios_dir,
        'output_dir': output_dir,
        'generation_type': generation_type,  # 记录生成方式
        'log': ''
    }
    
    # 在后台线程中启动任务
    thread = threading.Thread(
        target=run_video_generation,
        args=(images_dir, audios_dir, output_dir, task_id, generation_type)  # 传递generation_type参数
    )
    thread.daemon = True
    thread.start()
    
    flash(f'任务 #{task_id} 已提交', 'success')
    return redirect(url_for('task_status', task_id=task_id))


@app.route('/excel-process', methods=['POST'])
def excel_process():
    global excel_task_counter
    
    # 获取表单数据
    excel_path = request.form.get('excel_path')
    audios_dir = request.form.get('audios_dir')
    output_dir = request.form.get('output_dir', 'output')
    
    # 验证输入
    if not all([excel_path, audios_dir]):
        return jsonify({'success': False, 'error': '请填写所有必填字段'})
    
    if not os.path.exists(excel_path):
        return jsonify({'success': False, 'error': 'Excel文件不存在'})
    
    # 保存输入到缓存
    save_cached_inputs(
        excel_path=excel_path,
        audios_dir=audios_dir,
        output_dir=output_dir
    )
    
    # 创建新任务
    excel_task_counter += 1
    task_id = excel_task_counter
    
    excel_tasks[task_id] = {
        'id': task_id,
        'status': 'pending',
        'message': '任务已提交，等待执行...',
        'excel_path': excel_path,
        'audios_dir': audios_dir,
        'output_dir': output_dir,
        'log': ''
    }
    
    # 在后台线程中启动任务（直接使用新方法）
    thread = threading.Thread(
        target=run_excel_task_new_method,
        args=(excel_path, audios_dir, output_dir, task_id)
    )
    thread.daemon = True
    thread.start()
    
    return jsonify({'success': True, 'task_id': task_id})


@app.route('/local-file-process', methods=['POST'])
def local_file_process():
    global local_task_counter
    
    # 获取表单数据
    prompt = request.form.get('prompt')
    negative_prompt = request.form.get('negative_prompt', '')
    resolution = request.form.get('resolution', '720P')
    
    # 验证输入
    if not prompt:
        flash('请输入视频描述', 'error')
        return redirect(url_for('local_file'))
    
    # 检查是否有上传文件
    if 'image_file' not in request.files:
        flash('请选择图片文件', 'error')
        return redirect(url_for('local_file'))
    
    image_file = request.files['image_file']
    if image_file.filename == '':
        flash('请选择图片文件', 'error')
        return redirect(url_for('local_file'))
    
    # 保存输入到缓存
    save_cached_inputs(
        prompt=prompt,
        negative_prompt=negative_prompt,
        resolution=resolution
    )
    
    # 创建临时目录保存上传的文件
    temp_dir = os.path.join(tempfile.gettempdir(), 'i2v_uploads')
    os.makedirs(temp_dir, exist_ok=True)
    
    # 生成唯一文件名
    filename = f"{uuid.uuid4().hex}_{image_file.filename}"
    file_path = os.path.join(temp_dir, filename)
    
    # 保存上传的文件
    image_file.save(file_path)
    
    # 创建新任务
    local_task_counter += 1
    task_id = local_task_counter
    
    local_tasks[task_id] = {
        'id': task_id,
        'status': 'pending',
        'message': '任务已提交，等待执行...',
        'prompt': prompt,
        'negative_prompt': negative_prompt,  # 添加negative_prompt到任务信息中
        'resolution': resolution,
        'file_path': file_path,
        'log': '',
        'video_url': None
    }
    
    # 在后台线程中启动任务
    thread = threading.Thread(
        target=run_local_file_generation,
        args=(file_path, prompt, resolution, task_id, negative_prompt)  # 传递negative_prompt参数
    )
    thread.daemon = True
    thread.start()
    
    flash(f'任务 #{task_id} 已提交', 'success')
    return redirect(url_for('local_file_task_status', task_id=task_id))


@app.route('/image-edit-process', methods=['POST'])
def image_edit_process():
    global image_edit_task_counter
    
    # 获取表单数据
    prompt = request.form.get('prompt')
    negative_prompt = request.form.get('negative_prompt', '')
    
    # 验证输入
    if not prompt:
        return jsonify({'success': False, 'error': '请输入编辑描述'})
    
    # 检查是否有上传文件
    if 'image_file' not in request.files:
        return jsonify({'success': False, 'error': '请选择图片文件'})
    
    image_file = request.files['image_file']
    if image_file.filename == '':
        return jsonify({'success': False, 'error': '请选择图片文件'})
    
    # 保存输入到缓存
    save_cached_inputs(
        prompt=prompt,
        negative_prompt=negative_prompt
    )
    
    # 创建临时目录保存上传的文件
    temp_dir = os.path.join(tempfile.gettempdir(), 'image_edit_uploads')
    os.makedirs(temp_dir, exist_ok=True)
    
    # 生成唯一文件名
    filename = f"{uuid.uuid4().hex}_{image_file.filename}"
    file_path = os.path.join(temp_dir, filename)
    
    # 保存上传的文件
    image_file.save(file_path)
    
    # 创建新任务
    image_edit_task_counter += 1
    task_id = image_edit_task_counter
    
    image_edit_tasks[task_id] = {
        'id': task_id,
        'status': 'pending',
        'message': '任务已提交，等待执行...',
        'prompt': prompt,
        'negative_prompt': negative_prompt,
        'file_path': file_path,
        'output_path': None
    }
    
    # 在后台线程中启动任务
    thread = threading.Thread(
        target=run_image_edit_task,
        args=(file_path, prompt, negative_prompt, task_id)
    )
    thread.daemon = True
    thread.start()
    
    return jsonify({'success': True, 'task_id': task_id})


@app.route('/image-edit-task/<int:task_id>')
def image_edit_task_status(task_id):
    if task_id not in image_edit_tasks:
        flash('任务不存在', 'error')
        return redirect(url_for('image_edit'))
    
    return render_template('image_edit_task_status.html', task=image_edit_tasks[task_id])


@app.route('/excel-task/<int:task_id>')
def excel_task_status(task_id):
    if task_id not in excel_tasks:
        flash('任务不存在', 'error')
        return redirect(url_for('excel_processor'))
    
    return render_template('excel_task_status.html', task=excel_tasks[task_id])


@app.route('/api/image-edit-task/<int:task_id>')
def api_image_edit_task_status(task_id):
    if task_id not in image_edit_tasks:
        return jsonify({'error': 'Task not found'}), 404
    
    return jsonify(image_edit_tasks[task_id])


@app.route('/api/excel-task/<int:task_id>')
def api_excel_task_status(task_id):
    if task_id not in excel_tasks:
        return jsonify({'error': 'Task not found'}), 404
    
    task = excel_tasks[task_id]
    # 检查是否有欠费错误
    arrearage_error = False
    if 'results' in task and isinstance(task['results'], dict):
        arrearage_error = task['results'].get('has_arrearage', False)
    
    # 构建响应数据
    response_data = task.copy()
    response_data['arrearage_error'] = arrearage_error
    
    return jsonify(response_data)


@app.route('/local-file-task/<int:task_id>')
def local_file_task_status(task_id):
    if task_id not in local_tasks:
        flash('任务不存在', 'error')
        return redirect(url_for('local_file'))
    
    return render_template('local_file_task_status.html', task=local_tasks[task_id])


@app.route('/task/<int:task_id>')
def task_status(task_id):
    if task_id not in tasks:
        flash('任务不存在', 'error')
        return redirect(url_for('index'))
    
    return render_template('task_status.html', task=tasks[task_id])


@app.route('/tasks')
def task_list():
    return render_template('task_list.html', tasks=tasks)


@app.route('/api/task/<int:task_id>')
def api_task_status(task_id):
    if task_id not in tasks:
        return jsonify({'error': 'Task not found'}), 404
    
    task = tasks[task_id]
    # 明确返回所需字段，确保 generation_type 不会被遗漏
    return jsonify({
        'id': task['id'],
        'status': task['status'],
        'message': task['message'],
        'images_dir': task['images_dir'],
        'audios_dir': task['audios_dir'],
        'output_dir': task['output_dir'],
        'generation_type': task.get('generation_type', 'ai'),  # 确保默认值
        'log': task['log']
    })


@app.route('/api/local-file-task/<int:task_id>')
def api_local_file_task_status(task_id):
    if task_id not in local_tasks:
        return jsonify({'error': 'Task not found'}), 404
    
    # 确保返回 negative_prompt 信息
    return jsonify(local_tasks[task_id])


@app.route('/t2v')
def t2v():
    cached_inputs = load_cached_inputs()
    return render_template('t2v.html', cached_inputs=cached_inputs)


@app.route('/t2v-process', methods=['POST'])
def t2v_process():
    global t2v_task_counter
    
    # 获取表单数据
    team_a = request.form.get('team_a')
    team_b = request.form.get('team_b')
    match_time = request.form.get('match_time')
    match_name = request.form.get('match_name')
    output_dir = request.form.get('output_dir', 'output/videos')
    
    # 验证输入
    if not all([team_a, team_b, match_time, match_name]):
        return jsonify({'success': False, 'error': 'Vui lòng điền đầy đủ thông tin'})
    
    # 保存输入到缓存
    save_cached_inputs(
        team_a=team_a,
        team_b=team_b,
        match_time=match_time,
        match_name=match_name,
        output_dir=output_dir
    )
    
    # 创建新任务
    t2v_task_counter += 1
    task_id = t2v_task_counter
    
    t2v_tasks[task_id] = {
        'id': task_id,
        'status': 'pending',
        'message': 'Tác vụ đã được gửi, đang chờ thực thi...',
        'team_a': team_a,
        'team_b': team_b,
        'match_time': match_time,
        'match_name': match_name,
        'output_dir': output_dir,
        'log': ''
    }
    
    # 在后台线程中启动任务
    thread = threading.Thread(
        target=run_t2v_task,
        args=(team_a, team_b, match_time, match_name, output_dir, task_id)
    )
    thread.daemon = True
    thread.start()
    
    return jsonify({'success': True, 'task_id': task_id})


@app.route('/t2v-task/<int:task_id>')
def t2v_task_status(task_id):
    # 检查任务是否存在
    if task_id not in t2v_tasks:
        return jsonify({'error': 'Task not found'}), 404
    
    # Nếu làAJAX请求，返回JSON数据
    if request.headers.get('Accept') == 'application/json':
        return jsonify(t2v_tasks[task_id])
    
    # 否则显示模板
    return render_template('t2v_task_status.html', task=t2v_tasks[task_id])


@app.route('/api/t2v-task/<int:task_id>')
def api_t2v_task_status(task_id):
    if task_id not in t2v_tasks:
        return jsonify({'error': 'Task not found'}), 404
    
    return jsonify(t2v_tasks[task_id])


@app.route('/t2v-excel-process', methods=['POST'])
def t2v_excel_process():
    global t2v_task_counter
    
    # 获取表单数据
    excel_path = request.form.get('excel_path')
    output_dir = request.form.get('output_dir', 'output/videos')
    
    # 验证输入
    if not excel_path:
        return jsonify({'success': False, 'error': 'Vui lòng cung cấp đường dẫn tệp Excel'})
    
    if not os.path.exists(excel_path):
        return jsonify({'success': False, 'error': 'Tệp Excel không tồn tại'})
    
    # 保存输入到缓存
    save_cached_inputs(
        excel_path=excel_path,
        output_dir=output_dir
    )
    
    # 创建新任务
    t2v_task_counter += 1
    task_id = t2v_task_counter
    
    t2v_tasks[task_id] = {
        'id': task_id,
        'status': 'pending',
        'message': 'Tác vụ đã được gửi, đang chờ thực thi...',
        'excel_path': excel_path,
        'output_dir': output_dir,
        'type': 'excel',  # 标记为Excel处理任务
        'log': ''
    }
    
    # 在后台线程中启动任务
    thread = threading.Thread(
        target=run_t2v_excel_task,
        args=(excel_path, output_dir, task_id)
    )
    thread.daemon = True
    thread.start()
    
    return jsonify({'success': True, 'task_id': task_id})


@app.route('/female-portrait-video')
def female_portrait_video():
    cached_inputs = load_cached_inputs()
    return render_template('female_portrait_video.html', cached_inputs=cached_inputs)


@app.route('/female-portrait-video-process', methods=['POST'])
def female_portrait_video_process():
    global female_video_task_counter
    
    # 获取表单数据
    count = request.form.get('count', 1, type=int)
    output_dir = request.form.get('output_dir', 'output/female_videos')
    
    # 验证输入
    if count <= 0 or count > 10:
        return jsonify({'success': False, 'error': 'Số lượng tạo phải nằm trong khoảng 1-10'})
    
    # 检查是否有上传文件
    if 'image_file' not in request.files:
        return jsonify({'success': False, 'error': 'Vui lòng chọn tệp hình ảnh'})
    
    image_file = request.files['image_file']
    if image_file.filename == '':
        return jsonify({'success': False, 'error': 'Vui lòng chọn tệp hình ảnh'})
    
    # 保存输入到缓存
    save_cached_inputs(
        count=count,
        output_dir=output_dir
    )
    
    # 保存上传的图片
    import uuid
    image_filename = f"female_portrait_{uuid.uuid4().hex[:8]}{Path(image_file.filename).suffix}"
    image_path = os.path.join("temp", image_filename)
    os.makedirs("temp", exist_ok=True)
    image_file.save(image_path)
    
    # 创建新任务
    female_video_task_counter += 1
    task_id = female_video_task_counter
    
    female_video_tasks[task_id] = {
        'id': task_id,
        'status': 'pending',
        'message': 'Nhiệm vụ đã được gửi, đang chờ thực thi...',
        'count': count,
        'output_dir': output_dir,
        'image_path': image_path,
        'log': ''
    }
    
    # 在后台线程中启动任务
    thread = threading.Thread(
        target=run_female_video_task,
        args=(image_path, count, output_dir, task_id)
    )
    thread.daemon = True
    thread.start()
    
    return jsonify({'success': True, 'task_id': task_id})


@app.route('/female-video-task/<int:task_id>')
def female_video_task_status(task_id):
    if task_id not in female_video_tasks:
        flash('任务不存在', 'error')
        return redirect(url_for('female_portrait_video'))
    
    return render_template('female_video_task_status.html', task=female_video_tasks[task_id])


@app.route('/api/female-video-task/<int:task_id>')
def api_female_video_task_status(task_id):
    if task_id not in female_video_tasks:
        return jsonify({'error': 'Task not found'}), 404
    
    return jsonify(female_video_tasks[task_id])


@app.route('/download')
def download_video():
    filepath = request.args.get('filepath')
    if not filepath or not os.path.exists(filepath):
        flash('文件不存在', 'error')
        return redirect(url_for('index'))
    
    return send_file(filepath, as_attachment=True)


@app.route('/keyword-configs')
def keyword_configs():
    """关键词配置管理页面"""
    try:
        db = VideoDatabase()
        configs = db.get_keyword_configs(enabled_only=False)
        return render_template('keyword_configs.html', configs=configs)
    except Exception as e:
        flash(f'加载配置失败: {str(e)}', 'error')
        return redirect(url_for('index'))


@app.route('/keyword-configs/add', methods=['GET', 'POST'])
def add_keyword_config():
    """添加关键词配置"""
    if request.method == 'POST':
        try:
            keyword = request.form.get('keyword', '').strip()
            count = int(request.form.get('count', 5))
            enabled = bool(request.form.get('enabled', False))
            platform = request.form.get('platform', 'douyin').strip()
            source_lang = request.form.get('source_lang', 'zh').strip()
            target_lang = request.form.get('target_lang', 'vi').strip()
            
            if not keyword:
                flash('关键词不能为空', 'error')
                return redirect(url_for('add_keyword_config'))
            
            db = VideoDatabase()
            success = db.add_keyword_config(keyword, count, enabled, platform, source_lang, target_lang)
            if success:
                flash('添加成功', 'success')
            else:
                flash('添加失败，关键词可能已存在', 'error')
                
        except Exception as e:
            flash(f'添加失败: {str(e)}', 'error')
        
        return redirect(url_for('keyword_configs'))
    
    return render_template('add_keyword_config.html')


@app.route('/keyword-configs/edit/<int:config_id>', methods=['GET', 'POST'])
def edit_keyword_config(config_id):
    """编辑关键词配置"""
    db = VideoDatabase()
    
    if request.method == 'POST':
        try:
            keyword = request.form.get('keyword', '').strip()
            count = int(request.form.get('count', 5))
            enabled = bool(request.form.get('enabled', False))
            platform = request.form.get('platform', 'douyin').strip()
            source_lang = request.form.get('source_lang', 'zh').strip()
            target_lang = request.form.get('target_lang', 'vi').strip()
            
            if not keyword:
                flash('关键词不能为空', 'error')
                return redirect(url_for('edit_keyword_config', config_id=config_id))
            
            success = db.update_keyword_config(config_id, keyword=keyword, count=count, 
                                             enabled=enabled, platform=platform,
                                             source_lang=source_lang, target_lang=target_lang)
            if success:
                flash('更新成功', 'success')
            else:
                flash('更新失败', 'error')
                
        except Exception as e:
            flash(f'更新失败: {str(e)}', 'error')
        
        return redirect(url_for('keyword_configs'))
    
    # GET请求 - 显示编辑表单
    try:
        configs = db.get_keyword_configs(enabled_only=False)
        config = next((c for c in configs if c.id == config_id), None)
        if not config:
            flash('配置不存在', 'error')
            return redirect(url_for('keyword_configs'))
        return render_template('edit_keyword_config.html', config=config)
    except Exception as e:
        flash(f'加载配置失败: {str(e)}', 'error')
        return redirect(url_for('keyword_configs'))


@app.route('/keyword-configs/delete/<int:config_id>', methods=['POST'])
def delete_keyword_config(config_id):
    """删除关键词配置"""
    try:
        db = VideoDatabase()
        success = db.delete_keyword_config(config_id)
        if success:
            flash('删除成功', 'success')
        else:
            flash('删除失败', 'error')
    except Exception as e:
        flash(f'删除失败: {str(e)}', 'error')
    
    return redirect(url_for('keyword_configs'))


def run_keyword_task_in_background(keyword_config):
    """
    在后台线程中执行关键词任务
    
    Args:
        keyword_config: 关键词配置对象
    """
    from datetime import datetime
    from src.processors.tikhub_downloader import TikHubDownloader
    
    print(f"[{datetime.now()}] 开始执行关键词任务: {keyword_config.keyword} (数量: {keyword_config.count})")
    
    try:
        # 初始化下载器
        downloader = TikHubDownloader()
        
        # 执行视频收集和处理任务
        # 使用默认语言: source_lang=zh, target_lang=vi
        result = downloader.collect_and_process_videos(
            keyword=keyword_config.keyword,
            count=keyword_config.count,
            platform=keyword_config.platform,
            source_lang=keyword_config.source_lang,
            target_lang=keyword_config.target_lang
        )
        
        # 更新最后运行时间
        db = VideoDatabase()
        db.update_keyword_last_run_time(keyword_config.keyword)
        
        if result["success"]:
            print(f"[{datetime.now()}] 关键词任务执行成功: {keyword_config.keyword}")
            print(f"  - 收集到视频数量: {len(result.get('collected_videos', []))}")
            print(f"  - 实际处理视频数量: {len(result.get('filtered_videos', []))}")
        else:
            print(f"[{datetime.now()}] 关键词任务执行失败: {keyword_config.keyword}")
            print(f"  - 错误信息: {result.get('message', '未知 lỗi')}")
            
    except Exception as e:
        print(f"[{datetime.now()}] 执行关键词任务时出错: {keyword_config.keyword}")
        print(f"  - 错误详情: {e}")
        import traceback
        traceback.print_exc()


@app.route('/keyword-configs/trigger/<int:config_id>', methods=['POST'])
def trigger_keyword_config(config_id):
    """立即触发关键词配置任务"""
    try:
        # 获取关键词配置
        db = VideoDatabase()
        configs = db.get_keyword_configs(enabled_only=False)
        config = next((c for c in configs if c.id == config_id), None)
        
        if not config:
            flash('配置不存在', 'error')
            return redirect(url_for('keyword_configs'))
            
        if not config.enabled:
            flash('配置未启用', 'error')
            return redirect(url_for('keyword_configs'))
        
        # 在后台线程中执行任务
        thread = threading.Thread(target=run_keyword_task_in_background, args=(config,))
        thread.daemon = True
        thread.start()
        
        flash('任务已启动，请在日志中查看执行情况', 'success')
    except Exception as e:
        flash(f'触发失败: {str(e)}', 'error')
    
    return redirect(url_for('keyword_configs'))


@app.route('/api/keyword-configs')
def api_keyword_configs():
    """API接口 - 获取所有关键词配置"""
    try:
        db = VideoDatabase()
        configs = db.get_keyword_configs(enabled_only=False)
        # 转换为字典列表
        config_list = []
        for config in configs:
            config_list.append({
                'id': config.id,
                'keyword': config.keyword,
                'count': config.count,
                'enabled': config.enabled,
                'last_run_time': config.last_run_time,
                'create_time': config.create_time,
                'platform': config.platform,
                'source_lang': config.source_lang,
                'target_lang': config.target_lang
            })
        return jsonify({'success': True, 'data': config_list})
    except Exception as e:
        return jsonify({'success': False, 'message': str(e)}), 500


# 初始化Sora2视频处理器
sora2_handler = Sora2VideoHandler()

# Sora2视频生成相关路由
@app.route('/sora2')
def sora2_video():
    """Sora2视频生成页面"""
    return render_template('sora2_video.html')

@app.route('/sora2/generate', methods=['POST'])
def sora2_generate():
    """处理Sora2视频生成请求"""
    return sora2_handler.generate_video()

@app.route('/sora2/task_status/<task_id>')
def sora2_task_status(task_id):
    """获取Sora2任务状态"""
    return sora2_handler.get_task_status(task_id)

@app.route('/sora2/video/<filename>')
def sora2_serve_video(filename):
    """提供视频文件服务"""
    return sora2_handler.serve_video(filename)

@app.route('/sora2/download/<filename>')
def sora2_download_video(filename):
    """提供视频文件下载"""
    return sora2_handler.download_video(filename)


@app.route('/batch-lipsync')
def batch_lipsync():
    """批量对口型视频处理页面"""
    # 从缓存中获取输入
    cached_inputs = {}
    try:
        if os.path.exists('input_cache.json'):
            with open('input_cache.json', 'r', encoding='utf-8') as f:
                cached_inputs = json.load(f)
    except Exception as e:
        print(f"读取缓存输入时出错: {e}")
    
    return render_template('batch_lipsync_video.html', cached_inputs=cached_inputs)


@app.route('/batch-lipsync-process', methods=['POST'])
def batch_lipsync_process():
    """处理批量对口型视频生成请求"""
    global batch_lipsync_task_counter, batch_lipsync_tasks
    
    try:
        # 获取表单数据
        excel_path = request.form.get('excel_path', '').strip()
        output_dir = request.form.get('output_dir', '').strip()
        
        if not excel_path:
            return jsonify({'success': False, 'error': 'Excel文件路径不能为空'})
        
        # 增加任务计数器
        batch_lipsync_task_counter += 1
        task_id = str(batch_lipsync_task_counter)
        
        # 保存任务信息
        task_info = {
            'excel_path': excel_path,
            'output_dir': output_dir,
            'status': 'pending',
            'progress': 0,
            'total_videos': 0,
            'completed_videos': 0,
            'results': [],
            'start_time': None,
            'end_time': None
        }
        
        batch_lipsync_tasks[task_id] = task_info
        
        # 在后台线程中处理任务
        thread = threading.Thread(
            target=run_batch_lipsync_task,
            args=(task_id, excel_path, output_dir)
        )
        thread.daemon = True
        thread.start()
        
        # 保存输入到缓存
        try:
            cached_inputs = {
                'excel_path': excel_path,
                'output_dir': output_dir
            }
            with open('input_cache.json', 'w', encoding='utf-8') as f:
                json.dump(cached_inputs, f, ensure_ascii=False, indent=2)
        except Exception as e:
            print(f"保存缓存输入时出错: {e}")
        
        return jsonify({'success': True, 'task_id': task_id})
        
    except Exception as e:
        return jsonify({'success': False, 'error': str(e)})


def run_batch_lipsync_task(task_id: str, excel_path: str, output_dir: str):
    """运行批量对口型视频处理任务"""
    global batch_lipsync_tasks
    
    try:
        # 更新任务状态
        batch_lipsync_tasks[task_id].update({
            'status': 'running',
            'start_time': time.time()
        })
        
        # 导入处理器
        from src.processors.batch_lipsync_processor import process_batch_lipsync
        
        # 处理任务
        result = process_batch_lipsync(excel_path, output_dir, task_id)
        
        # 更新任务结果
        batch_lipsync_tasks[task_id].update({
            'status': 'completed' if result['success'] else 'failed',
            'end_time': time.time(),
            'total_videos': result.get('total_videos', 0),
            'completed_videos': result.get('completed_videos', 0),
            'results': result.get('results', [])
        })
        
        if not result['success']:
            batch_lipsync_tasks[task_id]['error'] = result.get('error', '处理失败')
            
    except Exception as e:
        batch_lipsync_tasks[task_id].update({
            'status': 'failed',
            'end_time': time.time(),
            'error': str(e)
        })


@app.route('/batch-lipsync-task/<task_id>')
def batch_lipsync_task_status_page(task_id):
    """批量对口型视频任务状态页面"""
    global batch_lipsync_tasks
    
    if task_id not in batch_lipsync_tasks:
        flash('任务不存在', 'error')
        return redirect(url_for('index'))
    
    task_info = batch_lipsync_tasks[task_id]
    return render_template('batch_lipsync_task_status.html', task_id=task_id, task_info=task_info)


@app.route('/batch-lipsync-task-status/<task_id>')
def batch_lipsync_task_status(task_id):
    """获取批量对口型视频任务状态"""
    global batch_lipsync_tasks
    
    if task_id not in batch_lipsync_tasks:
        return jsonify({'error': '任务不存在'}), 404
    
    task_info = batch_lipsync_tasks[task_id]
    
    # 计算进度百分比
    total_videos = task_info.get('total_videos', 0)
    completed_videos = task_info.get('completed_videos', 0)
    
    if total_videos > 0:
        progress = int((completed_videos / total_videos) * 100)
    else:
        progress = 0
    
    return jsonify({
        'status': task_info['status'],
        'progress': progress,
        'total_videos': total_videos,
        'completed_videos': completed_videos,
        'results': task_info.get('results', [])
    })


if __name__ == '__main__':
    # 设置标准输出和标准错误流的编码
    sys.stdout.reconfigure(encoding='utf-8')
    sys.stderr.reconfigure(encoding='utf-8')
    
    app.run(debug=False, host='0.0.0.0', port=15001)